﻿#region Header

/*
Behavioral Rating of Dancing Human Crowds based on Motion Patterns
By

Pascal Hauser 
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

and

Raphael Gfeller
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

*/

#endregion

#region Usings

using System;
using System.Drawing;
using Emgu.CV;
using Emgu.CV.Structure;
using paravili.Services;
using Sebarf.Services.Interfaces;

#endregion

namespace paravili.Steps {
	/// <summary>
	/// finds the difference between two images by calculation the difference between the color of frame n and frame n-1
	/// if the difference is between the two given thresholds (<see cref="MinThresholdValue"/> and <see cref="MaxThresholdValue"/>)
	/// than the point is marked as differently and is returned
	/// </summary>
	public class FindDifferencesBetweenImages : ProcessStepConverterWithMeasurement<Image<Lab, Byte>, ColoredPoint[,]> {
		#region Public Properties

		public Image<Lab, Byte> LastImage { get; private set; }

		[ConfigurableNumericValue(Name = "MinThresholdValue", RangeFrom = 1, RangeTo = 500)]
		public int MinThresholdValue { get; set; }

		[ConfigurableNumericValue(Name = "MaxThresholdValue", RangeFrom = 1, RangeTo = 500)]
		public int MaxThresholdValue { get; set; }

		[ServiceRequest]
		public IResetHandlerService ResetHandlerService {
			set { value.Reset += OnReset; }
		}

		[ConfigurabelBooleanValue(Name = "use the manhatten distance for computing the distance")]
		public bool UseManhattenDistanceMethod { get; set; }

		[ServiceRequest]
		public IImageValidRegionProvider ImageValidRegionProvider { get; set; }

		#endregion

		#region Public Methods

		public FindDifferencesBetweenImages() {
			MinThresholdValue = 50;
			MaxThresholdValue = 500;
			UseManhattenDistanceMethod = true;
		}

		#endregion

		#region Private Methods

		private ColoredPoint[,] FindCanditates(Image<Lab, Byte> first, Image<Lab, Byte> second) {
			var candidates = new ColoredPoint[first.Width, first.Height];
			var invalidRegion = ImageValidRegionProvider.InvalidRegionByRowAndColumn;
			//	LinkedList<KeyValuePair<Point, Lab>> list = new LinkedList<KeyValuePair<Point, Lab>> ();
			Image<Lab, byte> diff = first - second;
			int minTreshold = MinThresholdValue;
			int maxThreshold = MaxThresholdValue;

			byte[, ,] data = diff.Data;
			int rows = diff.Rows;
			int cols = diff.Cols;
			for (int col = 0; col < cols; col++) {
				for (int row = 0; row < rows; row++) {

					// ignore invalid regions
					if (invalidRegion[row, col]) {
						continue;
					}
					// http://farbe.wisotop.de/vonFarbeZuFarbe.shtml
					double colorDistance = 0;
					if (UseManhattenDistanceMethod) {
						colorDistance = Math.Abs(data[row, col, 0]) + Math.Abs(data[row, col, 1]) + Math.Abs(data[row, col, 2]);
					}
					else {
						colorDistance =
							Math.Pow(
								Math.Pow(data[row, col, 0], 2) + Math.Pow(data[row, col, 1], 2) + Math.Pow(data[row, col, 2], 2), 0.5);
					}

					if (minTreshold < colorDistance && colorDistance < maxThreshold) {
						candidates[col, row] = new ColoredPoint {
							P = new Point(col, row),
							Color = new Lab(data[row, col, 0], data[row, col, 1], data[row, col, 2])
						};
					}
				}
			}

			return candidates;
		}

		private void OnReset(Object sender, EventArgs args) {
			LastImage = null;
		}

		#endregion

		protected override ColoredPoint[,] Convert(Image<Lab, byte> toProcess) {
			if (LastImage == null) {
				LastImage = toProcess;
				return null;
			}
			if (!(LastImage.Width == toProcess.Width && LastImage.Height == toProcess.Height)) {
				LastImage = null;
				return null;
			}
			//return null;
			ColoredPoint[,] toReturn = FindCanditates(LastImage, toProcess);
			LastImage = toProcess.Copy();
			return toReturn;
		}
	}
}